package com.woniu.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.woniu.entity.CtfContainer;
import com.woniu.entity.CtfRange;
import com.woniu.entity.PageBean;
import com.woniu.mapper.CtfContainerMapper;
import com.woniu.service.CtfRangeService;
import com.woniu.mapper.CtfRangeMapper;
import com.woniu.utils.DockerUtils;
import com.woniu.utils.UploadMinioUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.codec.multipart.FilePart;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import reactor.core.publisher.Mono;

import java.io.IOException;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Date;
import java.util.List;

/**
 * @author nanqiao
 * @description 针对表【ctf_range(靶场)】的数据库操作Service实现
 * @createDate 2025-06-10 14:29:54
 */
@Service
@Slf4j  // 添加这一行
public class CtfRangeServiceImpl extends ServiceImpl<CtfRangeMapper, CtfRange>
        implements CtfRangeService {
    @Autowired
    private CtfRangeMapper ctfRangeMapper;
    @Autowired
    private DockerUtils dockerUtils;
    @Autowired
    private UploadMinioUtil uploadMinioUtil;
    @Autowired
    private CtfContainerMapper ctfContainerMapper;

    //插入镜像
    //    根据rangId查询difficultyId
    @Override
    public Integer selectDifficultyIdByRangeId(String rangeId) {
        return baseMapper.selectDifficultyIdByRangeId(rangeId);
    }

    /**
     * @param targetRangeFile 靶场镜像文件
     * @param targetRangeName 靶场名称
     * @param targetRangeType 靶场类型
     * @param difficultyId    难度id
     * @param description     描述
     * @author hq
     */
    @Override
    public void insertRange(FilePart targetRangeFile, String targetRangeName, String targetRangeType, int difficultyId, String flagPath, String description, String exposePort) {
//        Path tempPath = Paths.get("/tmp/uploads/" + targetRangeFile.filename());
//        targetRangeFile.transferTo(tempPath).block(); // 注意：需要block()因为是同步方法
        // 创建临时管道
        PipedOutputStream pos = new PipedOutputStream();
        PipedInputStream pis;
        try {
            pis = new PipedInputStream(pos);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        // 异步写入管道
        targetRangeFile.content()
                .subscribe(dataBuffer -> {
                            try {
                                pos.write(dataBuffer.asByteBuffer().array());
                            } catch (IOException e) {
                                throw new RuntimeException("写入管道失败", e);
                            } finally {
                                DataBufferUtils.release(dataBuffer);
                            }
                        },
                        error -> {
                            try {
                                pos.close();
                            } catch (IOException ignored) {
                            }
                            throw new RuntimeException("文件传输错误", error);
                        },
                        () -> {
                            try {
                                pos.close();
                            } catch (IOException ignored) {
                            }
                        });
        // 加载镜像
        CtfRange ctfRange = dockerUtils.loadDockerImageFromTar(pis, targetRangeName);
        //设置暴露端口
        ctfRange.setExposePort(exposePort);
        //设置靶场类型
        ctfRange.setRangeType(targetRangeType);
        //设置困难等级
        ctfRange.setDifficultyId(difficultyId);
        //设置描述
        ctfRange.setRangeDesc(description);
        //设置上传时间
        ctfRange.setUploadTime(new Date());
        //设置上传人
        ctfRange.setUploader("admin");
        //设置更新时间
        ctfRange.setUpdateTime(new Date());
        //设置更新人
        ctfRange.setUpdater("admin");
        //设置flag路劲
        ctfRange.setFlagPath(flagPath);
        ctfRangeMapper.insertCtf(ctfRange);
    }

    /**
     * @param rangeId
     * @param force   是否强制删除
     * @author hq
     */
    @Override
    public void deleteRange(String rangeId, boolean force) {
        // 先查再删，更安全
        CtfRange ctfRange = ctfRangeMapper.selectById(rangeId);
        if (ctfRange == null) {
            throw new RuntimeException("靶场不存在");
        }
        // 停止容器
        List<CtfContainer> containers = ctfContainerMapper.selectContainersByRangeId(rangeId);
        for (CtfContainer container : containers) {
            dockerUtils.stopAndRemoveContainer(container.getContainerId());
        }
        // 删除镜像
        dockerUtils.removeImage(ctfRange.getRangeName(), force); // 使用镜像名称删除
        // 删除数据库记录
        ctfRangeMapper.deleteByRangeId(rangeId);
    }


    /**
     * @param pageNum  页码
     * @param pageSize 每页记录数
     * @param range    筛选条件
     * @return
     * @author hq
     */
    @Override
    public PageBean<CtfRange> selectPage(Integer pageNum, Integer pageSize, CtfRange range) {
        // 创建 PageBean 对象
        PageBean<CtfRange> pg = new PageBean<>();

        // 开启分页查询
        PageHelper.startPage(pageNum, pageSize);
        List<CtfRange> ctfRanges = ctfRangeMapper.selectPageList(range);

        // 处理 rangeName，去除 ":latest"
        for (CtfRange ctfRange : ctfRanges) {
            if (ctfRange.getRangeName() != null && ctfRange.getRangeName().endsWith(":latest")) {
                ctfRange.setRangeName(ctfRange.getRangeName().replace(":latest", ""));
            }
        }
        PageInfo<CtfRange> pageInfo = new PageInfo<>(ctfRanges);
        pg.setTotal(pageInfo.getTotal());
        pg.setList(pageInfo.getList());

        return pg;
    }


    //    private void tryRemoveImage(String rangeId, boolean force) {
//        try {
//            dockerUtils.removeImage(rangeId, force);
//        } catch (Exception e) {
//            System.out.println("删除镜像失败 (rangeId: {}), 继续执行数据库删除");
//        }
//    }
    @Override
    public void addRange(CtfRange range) {
        ctfRangeMapper.insert(range);
    }

    @Override
    public void updateByRangeId(CtfRange range) {
        range.setUpdateTime(new Date());
        range.setUpdater("admin");
        ctfRangeMapper.updateByRangeId(range);
    }

    @Override
    public List<CtfRange> selectByCondition(CtfRange range) {
        return List.of();
    }

    @Override
    public List<CtfRange> selectPage() {
        return List.of();
    }

    /**
     * @Description 新增：实现多条件搜索靶场
     * @Param
     * @Return
     * @Author pml
     * @Date 2025/6/12 10:48
     */
    @Override
    public List<CtfRange> searchRanges(String keyword) {
        QueryWrapper<CtfRange> wrapper = new QueryWrapper<>();
        wrapper.like("range_name", keyword)    // 靶场名称
                .or().like("range_desc", keyword)  // 靶场描述
                .or().like("updater", keyword)    // 更新人
                .or().like("uploader", keyword);  // 上传人
        // 通过Mapper执行查询
        return ctfRangeMapper.selectList(wrapper);
    }

    @Override
    public Mono<String> uploadImageByRangeId(String rangeId, FilePart file) {
        log.info("开始上传文件，rangeId: {}", rangeId);

        return uploadMinioUtil.addDocuments(file)
                .flatMap(url -> {
                    // 查询靶场信息
                    return Mono.fromCallable(() -> ctfRangeMapper.selectById(rangeId))
                            .switchIfEmpty(Mono.error(new RuntimeException("靶场不存在")))
                            .flatMap(ctfRange -> {
                                // 更新 URL
                                ctfRange.setMinioUrl(url);
                                // 更新数据库
                                boolean success = ctfRangeMapper.updateByRangeId(ctfRange);
                                if (!success) {
                                    return Mono.error(new RuntimeException("数据库更新失败"));
                                }
                                return Mono.just(url);
                            });
                });
    }


}
